#pragma once

#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QPointer>
#include <QImage>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QVector>
#include <QFile>
#include <QTimer>
#include <QHBoxLayout>
#include <QSound>
#include <QFileDialog>
#include <QMenuBar>
#include <QMenu>
#include "chip8.hpp"
#include "imageview.h"

static QVector<QString> listEmbededRomPath = {
    ":/Tetris.ch8",
    ":/invaders.c8"
};


class MainWindow final: public QMainWindow{
Q_OBJECT
public:
    MainWindow(QWidget* parent=nullptr): QMainWindow(parent) {
        // 菜单栏
        registerActions();

        beepSound = new QSound(":/beep.wav", this);
        machine.initialize();
        imageScreen = QImage(64, 32, QImage::Format_RGB888);

        QWidget* centeralWidget = new QWidget(this);
        this->setCentralWidget(centeralWidget);
        QHBoxLayout* hbox1 = new QHBoxLayout(centeralWidget);
        imageView = new ImageView(this);
        hbox1->addWidget(imageView);

        loadGame(listEmbededRomPath[0]);

        QTimer* timerProcess = new QTimer(this);
        timerProcess->setInterval(1000 / 500);
        connect(timerProcess, &QTimer::timeout, this, &MainWindow::process);
        timerProcess->start();
        QTimer* timerDraw = new QTimer(this);
        timerDraw->setInterval(1000 / 60);
        connect(timerDraw, &QTimer::timeout, this, &MainWindow::process60);
        timerDraw->start();
    }

    virtual ~MainWindow() {

    }

    void loadGame(QString path) {
        QFile file(path);
        file.open(QIODevice::ReadOnly);
        QDataStream inputStream(&file);
        machine.loadGame(inputStream);
    }

protected:

    QSize sizeHint() const override {
        return QSize(800, 600);
    }

    void changeEvent(QEvent* event) override {
        QMainWindow::changeEvent(event);
        imageView->fitViewport();
    }

    void resizeEvent(QResizeEvent* event) override {
        QMainWindow::resizeEvent(event);
        imageView->fitViewport();
    }

    void keyPressEvent(QKeyEvent *event) override {
        int keyMachine = keyMapping(event->key());
        if (0 <= keyMachine && keyMachine < 16) {
            machine.keyPress(keyMachine); 
        }
        printf("keypress;\n");
    }

    void keyReleaseEvent(QKeyEvent* event) override {
        int keyMachine = keyMapping(event->key());
        if (0 <= keyMachine && keyMachine < 16)
            machine.keyRelease(keyMachine);
        printf("keyrelease;\n");
    }

    /*
    * 将键盘上的按键转化为机器的按键.
    */
    int keyMapping(int key) {
        // 电脑键盘的布局
        QVector<int> keyArray = {
            Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4,
            Qt::Key_Q, Qt::Key_W, Qt::Key_E, Qt::Key_R,
            Qt::Key_A, Qt::Key_S, Qt::Key_D, Qt::Key_F,
            Qt::Key_Z, Qt::Key_X, Qt::Key_C, Qt::Key_V
        };
        // Chip8机器的按键布局
        QVector<int> keyMachineArray = {
            0x1, 0x2, 0x3, 0xC,
            0x4, 0x5, 0x6, 0xD,
            0x7, 0x8, 0x9, 0xE,
            0xA, 0x0, 0xB, 0xF
        };
        int index = keyArray.indexOf(key);
        if (index < 0 || index >= 16)
            return -1;
        return keyMachineArray[index];
    }

private:
	Q_DISABLE_COPY(MainWindow);
    chip8::Chip8 machine;
    ImageView* imageView = nullptr;
    QImage imageScreen;
    QSound* beepSound = nullptr;

    /*
    * 以60赫兹运行的事务。
    */
    void process60() {
        machine.timerStep();
        if (machine.getSoundTimer()) {
            beepSound->play();
            printf("beep!\n");
        }
    }

    /*
    * 以500赫兹运行的事务。
    */ 
    void process() {
        auto ret = machine.emulateCycle();
        if (ret != chip8::Chip8Error::OK) {
            printf("opcode: 0x%x;\n", machine.getOpcode());
            printf("error code: %x;\n", (int)ret);
            exit(-1);
        }
        // printf("pc: 0x%04x;\n", machine.getPc());
        // printf("opcode: 0x%x\n", machine.getOpcode());
        if (machine.getDrawFlag()) {
            unsigned char* gfx = (unsigned char*) machine.getGfx();
            for (int y = 0; y < 32; ++y) {
                for (int x = 0; x < 64; ++x) {
                    int gray = gfx[y * 64 + x] ? 255 : 0;
                    imageScreen.setPixelColor(x, y, QColor(gray, gray, gray));
                }
            }
            machine.setDrawFlag(0);
            imageView->setImage(imageScreen, true);
        }
    }

    void registerActions() {
        printf("register menus.\n");
        QMenuBar* menu_bar = new QMenuBar(this);
        setMenuBar(menu_bar);

        QMenu* fileMenu = new QMenu("文件", menu_bar);
        auto openFile = new QAction(this);
        openFile->setText("打开文件");
        openFile->setStatusTip("打开ROM文件");
        connect(openFile, &QAction::triggered, this, &MainWindow::openFile);
        fileMenu->addAction(openFile);

        menu_bar->addMenu(fileMenu);
    }

    void openFile() {
        auto&& filename = QFileDialog::getOpenFileName(this, "打开ROM文件", ".", "ROM files (*.ch8 *.c8)");
        if (filename.isNull())
            return;
        machine.initialize();
        QFile romFile(filename);
        romFile.open(QIODevice::ReadOnly);
        QDataStream dataStream(&romFile);
        machine.loadGame(dataStream);
    }
};
